module DDS_CTL
(
	CLK,
	RSTn,
	CMDIN,
	DATIN,
	CS,
	WRn,
	RDn,
	//ADDR,
	RS,
	RAM_DAT,
	RAM_WEN,
	RAM_ADDR,
	RAM_CLK,
	LED_OUT
);


	input CLK;
	input RSTn;
	input [15:0]CMDIN;	//command input
	input [15:0]DATIN;	//data input
	input CS;				//Chip Select
	input WRn;				//write enable
	input RDn;				//read enable
	//input [2:0]ADDR;				//switch to write data or cmd
	input RS;
	
	
	//----------define data line's width-----------//
	parameter RAM_ADDR_WID = 8'd12;
	parameter RAM_DATA_WID = 8'd16;
	
	output RAM_CLK;
	output [RAM_ADDR_WID-1:0]RAM_ADDR;
	output [RAM_DATA_WID-1:0]RAM_DAT;
	output RAM_WEN;
	
	output LED_OUT;
	
	
	reg RAM_CLK;
	reg [RAM_ADDR_WID-1:0] RAM_ADDR;
	reg [RAM_DATA_WID-1:0] RAM_DAT;
	reg RAM_WEN;
	
	//reg LED_OUT;
	
	//----------parameter define-----------//
	//addr switch
	//parameter CMDIN = 3'd1;
	//command define
	//parameter Write
	
	//-----------get a clk posedge---------//
	wire rd;
	wire wr;
	reg rds1,rds2;
	reg wrs1,wrs2;
	
	always @ (posedge CLK or negedge RSTn)
	begin
		if(!RSTn)
		begin
			rds1 <= 1'b0;
			rds2 <= 1'b0;
			wrs1 <= 1'b0;
			wrs2 <= 1'b0;
		end
		else
		begin
			//rds1 <= !(CS & RDn);
			rds1 <= (CS | RDn);
			rds2 <= rds1;
			//wrs1 <= !(CS & WRn);
			wrs1 <= (CS | WRn);
			wrs2 <= wrs1;
		end
	end
	
	assign rd = (!rds1 & rds2);
	assign wr = (!wrs1 & wrs2);
	//-------------------------------------//
	
	//-------------get status--------------//
	wire RS;
	//assign RS = (ADDR[0] && (!ADDR[1]) && (!ADDR[2]));//input command while RS == 1
		
	/*------------status define------------*/	
	parameter DDS_DISABLE			=	16'd0;
	parameter DDS_ENABLE				=	16'd1;
	parameter DDS_PAUSE				=	16'd2;
	parameter DDS_RESET				=	16'd3;
	parameter SET_BUF_SIZE			=	16'd4;	//set ram buffer size
	parameter WR_FULL_DAT			=	16'd5;	//write full data of dds ram,addr increase automatic
	parameter WR_TO_RAM				=	16'd6; //keep this status while writing data to RAM --- reserved
	parameter SET_ADDR				=	16'd7;
	//parameter SET_WR_ADDR			=	16'd5;
	//parameter SET_RD_ADDR			=	16'd6;
	parameter WR_ONE_BYTE			=	16'd8;
	//parameter SET_FRE_RANGE		=	16'd9;
	parameter SET_FRE_PARA			=	16'd10;
	parameter SET_NONE				=	16'd11;	//doing nothing
	parameter SET_FRE_PARA_L16B	=	16'd12;
	parameter SET_FRE_PARA_H16B	=	16'd13;
	
	/*------------variable define----------*/
	//cmd part
	reg [15:0]ddsSta; //dds status
	reg [RAM_ADDR_WID-1:0]wr_addr;	//sram write/read address control
	reg [RAM_ADDR_WID-1:0]rd_addr;
	reg [RAM_ADDR_WID-1:0]ram_addr_set;
	reg dds_out_en;	//output enable
	reg ram_wr_en;		//sram write enable
	
	//data part
	reg [RAM_ADDR_WID-1:0]ram_data_set;
	reg [RAM_ADDR_WID-1:0]ram_buf_size;
	//reg [1:0]clk_range;		//0:unit 1:kilo 2:million
	reg [31:0]clk_para;			//set clk parameter : 32bits
	reg [1:0]clk_para_flag;		//0:none 1:lower 16bits 2:higher 16bits 3:set ok
	
	reg ram_wr_flag;//when there's something need to write to sram, set to 1
	
	/*------------cmd processing-----------*/
	always @ (posedge CLK or negedge RSTn)
	begin
		if(!RSTn)
		begin
			//cmd part initial
			dds_out_en <= 1'b0;
			ram_wr_en <= 1'b0;
			wr_addr <= {(RAM_ADDR_WID){1'd0}};
			rd_addr <= {(RAM_ADDR_WID){1'd0}};
			ddsSta <= DDS_DISABLE;
			
			//data part initial
			ram_data_set <= {(RAM_DATA_WID){1'd0}};
			//clk_range <= 2'd0;
			clk_para <= 32'd0;
			clk_para_flag <= 2'd0;
			ram_wr_flag <= 1'b0;
			ram_addr_set <= {(RAM_ADDR_WID){1'd0}};
		end
		else if((wr == 1'b1)&&(RS == 0))	//input command
		begin
			case(CMDIN)
				DDS_DISABLE:
				begin
					ddsSta <= DDS_DISABLE;
					dds_out_en <= 1'b0;
					wr_addr <= {(RAM_ADDR_WID){1'd0}};
					rd_addr <= {(RAM_ADDR_WID){1'd0}};
				end
				DDS_ENABLE:
				begin
					ddsSta <= DDS_ENABLE;
					dds_out_en <= 1'b1;
					rd_addr <= {(RAM_ADDR_WID){1'd0}};//reset and run
				end
				DDS_PAUSE:	//disable output while writting data to sram
				begin
					ddsSta <= DDS_PAUSE;
					dds_out_en <= 1'b0;
				end
				DDS_RESET:
				begin
					ddsSta <= DDS_DISABLE;
					wr_addr <= {(RAM_ADDR_WID){1'd0}};
					rd_addr <= {(RAM_ADDR_WID){1'd0}};
				end
				SET_BUF_SIZE:
				begin
					ddsSta <= SET_BUF_SIZE;
				end
				WR_FULL_DAT:
				begin
					ddsSta <= WR_FULL_DAT;
					dds_out_en <= 1'b0;
					ram_wr_en <= 1'b1;
					rd_addr <= {(RAM_ADDR_WID){1'd0}};
					//wr_addr <= {(RAM_ADDR_WID){(32'd2 << (RAM_ADDR_WID-1'b1))-1'b1}};	//due to the address auto increase
					//wr_addr <= 8'd255;
					wr_addr <= 12'd4095;
				end
				SET_ADDR:
				begin
					ddsSta <= SET_ADDR;
				end
				WR_ONE_BYTE:
				begin
					ddsSta <= WR_ONE_BYTE;
				end
				/*SET_FRE_RANGE:
				begin
					ddsSta <= SET_FRE_RANGE;
				end*/
				SET_FRE_PARA:
				begin
					ddsSta <= SET_FRE_PARA;//brgin to write data
					clk_para_flag <= 2'd1;
				end
				default:;
					//ddsSta <= SET_NONE;
			endcase
			ram_wr_flag <= 1'b0;
		end
		
		/*-----------data processing-----------*/
		else if((wr == 1'b1)&&(RS == 1))	//input data
		begin
		//	case(CMDIN)
			case (ddsSta)
				DDS_DISABLE, DDS_ENABLE, DDS_RESET:
				begin
					ram_data_set <= {(RAM_DATA_WID){1'd0}};
				end
				DDS_RESET:
				begin
					ram_data_set <= {(RAM_DATA_WID){1'd0}};
					//clk_range <= 2'd0;
					clk_para <= 32'd0;
				end
				SET_BUF_SIZE:
				begin
					ram_buf_size <= (DATIN - 1'b1);
				end
				WR_FULL_DAT:
				begin
					if(wr_addr == ram_buf_size)
					begin
						wr_addr = 12'd4095;
						//wr_addr = {(RAM_ADDR_WID){(32'd2 << (RAM_ADDR_WID-1'b1))-1'b1}};
					end
					ram_data_set <= DATIN;
					wr_addr <= wr_addr + 1'b1;	//auto increase address
				end
				SET_ADDR:
				begin
					ram_addr_set <= DATIN[RAM_ADDR_WID-1:0];
				end
				WR_ONE_BYTE:
				begin
					ram_data_set <= DATIN;
				end
				/*SET_FRE_RANGE:
				begin
					//clk_range <= DATIN;
				end*/
				SET_FRE_PARA:
				begin
					if(clk_para_flag == 2'd1)
					begin
						clk_para[15:0] <= DATIN;
						clk_para_flag <= 2'd2;
					end
					else if(clk_para_flag == 2'd2)
					begin
						clk_para[31:16] <= DATIN;
						clk_para_flag <= 2'd3;
					end
				end
					
				default:;
			endcase
			ram_wr_flag <= 1'b1;
			/*if((RS == SET_BUF_SIZE) || (RS == WR_FULL_DAT) 
				|| (RS == SET_ADDR) || (RS == WR_ONE_BYTE) 
				|| (RS == SET_FRE_RANGE) || (RS == SET_FRE_PARA))
				ram_wr_flag <= 1'b1;
			else
				ram_wr_flag <= 1'b0;*/
		end
		/*else
			ram_wr_flag <= 1'b0;*/
	end
	
	/*------------variable define----------*/
	reg ram_clk_div;	// CLK/2 = 100MHz
	reg [31:0]ram_clk_counter;
	reg [31:0]ram_clk_limit;
	
	/*-------------ram control-------------*/
	always @ (posedge CLK or negedge RSTn)
	begin
		if(!RSTn)
		begin
			ram_clk_div <= 1'b0;	
			ram_clk_counter <= 32'd0;
			ram_clk_limit <= 32'd0;
			RAM_DAT <= {(RAM_DATA_WID){1'd0}};
			RAM_ADDR <= {(RAM_ADDR_WID){1'd0}};
			RAM_CLK <= 1'b0;
			RAM_WEN <= 1'b0;
		end
		else	//CLK_IN
		begin
			//
			if(ram_clk_div)	
			begin
				//preparing data and waiting for the posedge
				if(ram_wr_flag) 
				begin
					case (ddsSta)
						WR_FULL_DAT, WR_ONE_BYTE:	//write data to sram
						begin
							RAM_DAT <= ram_data_set; 
							RAM_ADDR <= wr_addr;
						end
						SET_ADDR:
							RAM_ADDR <= ram_addr_set;
						SET_FRE_PARA:
						begin
							if(clk_para_flag == 2'd3)
							ram_clk_limit <= clk_para;
						end
						default:;
					endcase
					//RAM_WEN <= 1'b1;
					//ram_wr_flag <= 1'b0; //clear writing flag;
				end
				//else
				//	RAM_WEN <= 1'b0;
				if((ddsSta == WR_FULL_DAT) || (ddsSta == WR_ONE_BYTE))
					RAM_WEN <= 1'b1;
				else
					RAM_WEN <= 1'b0;
				
				//Routine operation
				/*if(ram_wr_flag)
				begin
					RAM_ADDR <= RAM_ADDR + 1'd1;z
					if(RAM_ADDR > ram_buf_size)
						RAM_ADDR <= 16'd0;
				end
				else */if(dds_out_en && (!ram_wr_flag))
				begin
					ram_clk_counter <= ram_clk_counter + 1'b1;
					if(ram_clk_counter >= ram_clk_limit)
					begin
						ram_clk_counter <= 32'd0;
						/*rd_addr <= rd_addr + 1'b1;
						if(rd_addr > ram_buf_size)
							rd_addr <= {(RAM_ADDR_WID){1'd0}};*/
						RAM_ADDR <= RAM_ADDR + 1'd1;
						if(RAM_ADDR >= ram_buf_size)
							RAM_ADDR <= 16'd0;
						//RAM_ADDR <= rd_addr;
					end
					//else
					//begin
					//	ram_clk_counter <= ram_clk_counter + 1'b1;
					//end
				end
			end
			//clock toggling and output
			//RAM_ADDR <= rd_addr;
			RAM_CLK <= ram_clk_div;
			ram_clk_div <= ~ram_clk_div;
			
		end
	end
	
	/*-------------LED control-------------*/
	reg [21:0]led_counter;
	reg led_output;
	wire led_clk;
	
	parameter LED_CNT_PARA = 24'd10000000;
	
	assign led_clk = ram_clk_div;
	
	//always @ (posedge ram_clk_div or negedge RSTn)
	always @ (posedge led_clk or negedge RSTn)
	if(!RSTn)
	begin
		led_output <= 1'b0;
		led_counter <= 22'd0;
	end
	else if(dds_out_en)
	begin
		if(led_counter == 22'd0)
		begin
			//led_counter <= 24'd0;
			led_output <= ~led_output;
		end
		led_counter <= led_counter + 1'd1;
	end
	else if(!dds_out_en)
	begin
		led_output <= 1'b0;
		led_counter <= 22'd0;
	end
	
	assign LED_OUT = led_output;
	
endmodule
